package io.renren.modules.oatools.utils.http_tepm;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import groovy.lang.GroovyShell;
import groovy.lang.Script;
import io.renren.modules.oatools.dto.CallOrderSendDTO;
import okhttp3.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.SocketTimeoutException;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

public class OAHttpRequestInterface {
    private static final Logger log = LoggerFactory.getLogger(OAHttpRequestInterface.class);

    public static void main(String[] args) throws SQLException {

//        getHttpRequestSend("my_system","test");
    }

    /*public List<HttpRequestSend> getHttpRequestSend(String sysName, String invokeName) throws SQLException {
        DruidDataSource ds = new DruidDataSource();
        ds.setUrl("jdbc:sqlite:D:/OA/code/fy/fe/src/com/seeyon/apps/ext/finance/common/utils/invoke/oa_reflection.db");
        Connection conn = ds.getConnection();
        String sql = "select * from http_request_address where sys_name='" + sysName + "' and invoke_name='" + invokeName + "'";
        List<HttpRequestAddress> addressList = SqlExecutor.query(conn, sql, new BeanListHandler<HttpRequestAddress>(HttpRequestAddress.class));
        List<HttpRequestSend> sendList = new ArrayList<>();

        StringBuffer sendSqlIn = new StringBuffer();

        sendSqlIn.append("INSERT INTO\"http_request_send\" (\n" +
                "\t\"address_id\",\n" +
                "\t\"pid\",\n" +
                "\t\"sys_name\",\n" +
                "\t\"invoke_name\",\n" +
                "\t\"sort\",\n" +
                "\t\"env\",\n" +
                "\t\"request_headers\",\n" +
                "\t\"headers_groovy\",\n" +
                "\t\"request_params\",\n" +
                "\t\"params_groovy\",\n" +
                "\t\"request_body\",\n" +
                "\t\"body_groovy\",\n" +
                "\t\"request_url\",\n" +
                "\t\"request_client_grooby\",\n" +
                "\t\"request_before_groovy\",\n" +
                "\t\"request_after_groovy\",\n" +
                "\t\"request_type\",\n" +
                "\t\"comments\",\n" +
                "\t\"is_asyn\",\n" +
                "\t\"is_del\"\n" +
                ")\n" +
                "VALUES ");
        String sqls[] = new String[addressList.size()];
        final int[] index = {0};
        addressList.forEach((address) -> {
            HttpRequestSend send = new HttpRequestSend();
            send.setAddressId(address.getId());
            send.setPid(address.getPid());
            send.setSysName(address.getSysName());
            send.setInvokeName(address.getInvokeName());
            send.setSort(address.getSort());
            send.setEnv(address.getEnv());
            send.setRequestHeaders(address.getRequestHeaders());
            send.setHeadersGroovy(address.getHeadersGroovy());
            send.setRequestParams(address.getRequestParams());
            send.setParamsGroovy(address.getParamsGroovy());
            send.setRequestBody(address.getRequestBody());
            send.setBodyGroovy(address.getBodyGroovy());
            send.setRequestUrl(address.getRequestUrl());
            send.setRequestClientGrooby(address.getRequestClientGrooby());
            send.setRequestBeforeGroovy(address.getRequestBeforeGroovy());
            send.setRequestAfterGroovy(address.getRequestAfterGroovy());
            send.setRequestType(address.getRequestType());
            send.setComments(address.getComments());
            send.setIsAsyn(address.getIsAsyn());
            send.setIsDel(address.getIsDel());
            sendList.add(send);
            StringBuffer sendSql = new StringBuffer();
            sendSql.append(sendSqlIn).append("(" + address.getId() + "," + address.getPid() + ",'" + address.getSysName() + "','" + address.getInvokeName() + "'," + address.getSort() + ",'" + address.getEnv() + "','" + address.getRequestHeaders() + "','" + address.getHeadersGroovy() + "','" + address.getRequestParams() + "','" + address.getParamsGroovy() + "','" + address.getRequestBody() + "','" + address.getBodyGroovy() + "','" + address.getRequestUrl() + "','" + address.getRequestClientGrooby() + "','" + address.getRequestBeforeGroovy() + "','" + address.getRequestAfterGroovy() + "','" + address.getRequestType() + "','" + address.getComments() + "'," + address.getIsAsyn() + "," + address.getIsDel() + ");");

            sqls[index[0]] = sendSql.toString();
            index[0]++;
        });
        int[] sends = {};
        try {
            System.out.println(sqls);
            sends = SqlExecutor.executeBatch(conn, sqls);


        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
        System.out.println("sends:::" + sends);
        System.out.println(addressList);
        return sendList;
    }*/


    private static int serversLoadTimes = 0;
    private static int maxLoadTimes = 3; // 最大重连次数

    /**
     * @param upResponseBody 上游返回的body
     * @param requestSend    本次请求的参数
     * @return
     * @throws IOException
     */
    public boolean requestSend(String upResponseBody, CallOrderSendDTO requestSend) throws IOException {
        final Boolean isSuccess = true;
        log.info(JSONUtil.toJsonStr(requestSend));
        String requestUrl = null==requestSend.getRequestUrl()?"{}":requestSend.getRequestUrl();
        String requestParams = null==requestSend.getRequestParams()?"{}":requestSend.getRequestParams();
        String paramsGroovy = requestSend.getParamsGroovy();
        String requestHeaders = null==requestSend.getRequestHeaders()?"{}":requestSend.getRequestHeaders();
        String headersGroovy = requestSend.getHeadersGroovy();
        String bodyGroovy = requestSend.getBodyGroovy();
        String requestClientGrooby = requestSend.getRequestClientGroovy();
        String requestBeforeGroovy = requestSend.getRequestBeforeGroovy();
        String requestAfterGroovy = requestSend.getRequestAfterGroovy();
        String reqBody = null==requestSend.getRequestBody()?"{}":requestSend.getRequestBody();
        String requestType = null==requestSend.getRequestType()?"POST":requestSend.getRequestType();
        int isAsyn = null==requestSend.getIsAsyn()?0:Integer.valueOf(requestSend.getIsAsyn().toString());

        /**
         * 通过参数 params 拼接 URL
         */
        Map paramsMap = JSONUtil.toBean(requestParams, Map.class);
        HttpUrl.Builder urlBuilder = HttpUrl.parse(requestUrl).newBuilder();
        Map returnParamsMap = new HashMap();
        /** 执行Groovy脚本 **/
        if (StrUtil.isNotEmpty(paramsGroovy)) {
            Object[] executeParamsGroovy = new Object[]{upResponseBody, paramsMap, reqBody};
            Object executeParamsResult = executeGroovy(executeParamsGroovy, paramsGroovy);
            returnParamsMap = (Map) executeParamsResult;
        }
        returnParamsMap.forEach((key, value) -> {
            urlBuilder.addQueryParameter(key.toString(), value.toString()).build();
            log.info(key + "<<<<<<<>>>>>" + value);
        });
        HttpUrl url = urlBuilder.build();

        /**
         * 拼接 请求头
         */
        Headers.Builder headersBuilder = new Headers.Builder();
        Map headersMap = JSONUtil.toBean(requestHeaders, Map.class);

        /** 执行Groovy脚本 **/
        if (StrUtil.isNotEmpty(headersGroovy)) {
            Object[] executeHeadersGroovy = new Object[]{upResponseBody, headersMap, reqBody};
            Object executeHeadersResult = executeGroovy(executeHeadersGroovy, headersGroovy);
            headersMap = (Map) executeHeadersResult;
        }
        headersMap.forEach((key, value) -> {
            headersBuilder.add(key.toString(), value.toString());
            log.info(key.toString() + "《《《《《《《headers》》》》》》》》" + value.toString());
        });
        Headers headers = headersBuilder.build();
        log.info(headers.toString() + "<<<<<<请求地址》》》》》》》》》" + url);

        /**
         * OkHttpClient
         * 配置 HTTP 客户端: 用于设置网络请求的各种参数和行为
         * 构建模式: 使用 Builder 模式来逐步配置客户端选项clientBuilder
         * clientBuilder
         *     .connectTimeout(30, TimeUnit.SECONDS)    // 连接超时
         *     .readTimeout(30, TimeUnit.SECONDS)       // 读取超时
         *     .writeTimeout(30, TimeUnit.SECONDS);     // 写入超时
         *     .addInterceptor(interceptor)             // 应用拦截器
         *     .addNetworkInterceptor(networkInterceptor); // 网络拦截    .authenticator(authenticator)           // 认证器
         *     .sslSocketFactory(sslSocketFactory, trustManager)  // SSL 配置
         *     .hostnameVerifier(hostnameVerifier);    // 主机名验证
         *     .connectionPool(new ConnectionPool(5, 5, TimeUnit.MINUTES)); // 连接池
         *     .proxy(proxy)                           // 设置代理
         *     .proxyAuthenticator(proxyAuthenticator); // 代理认证
         */
        OkHttpClient.Builder clientBuilder = new OkHttpClient().newBuilder();
        if (StrUtil.isNotEmpty(requestClientGrooby)) {
            Object[] executeClientGroovy = new Object[]{upResponseBody};
            clientBuilder = (OkHttpClient.Builder) executeGroovy(executeClientGroovy, requestClientGrooby);
        }
        OkHttpClient client = clientBuilder.
                addInterceptor(new OAHttpNetworkInvokeIntercepter(requestSend))//拦截器
                .build();
//        client.authenticator();

        /**
         * 拼接 body
         */
        MediaType mediaType = MediaType.parse("application/json");
        RequestBody body = RequestBody.create(mediaType, reqBody);
        /** Body **/
        if (StrUtil.isNotEmpty(bodyGroovy)) {
            Object[] executeBodyGroovy = new Object[]{upResponseBody, url, reqBody, headers};
            body = (RequestBody) executeGroovy(executeBodyGroovy, bodyGroovy);
        }

        /**
         * 请求执行前
         */
        Request.Builder requestBuilder = new Request.Builder();
        if (StrUtil.isNotEmpty(requestBeforeGroovy)) {
            Object[] executeBeforeGroovy = new Object[]{upResponseBody, url, body, headers};
            requestBuilder = (Request.Builder) executeGroovy(executeBeforeGroovy, requestBeforeGroovy);
        }
        Request request = requestBuilder.url(url).build();
        if (requestType.equals("POST")) {
            request = requestBuilder.url(url).post(body).headers(headers).build();
        } else if (requestType.equals("GET")) {
            request = requestBuilder.url(url).get().headers(headers).build();
        }
        serversLoadTimes = 0;
        //默认为同步（异步获取不到返回值）
        if (requestSend.getIsAsyn() > 0) {
            Call call = client.newCall(request);//异步
            call.enqueue(new Callback() {
                @Override
                public void onFailure(Call call, IOException e) {
                    //失败回调
                    log.error("失败回调 onFailure: " + e.getMessage());
                    if (e instanceof SocketTimeoutException && serversLoadTimes < maxLoadTimes) {// 如果超时并未超过指定次数，则重新连接
                        serversLoadTimes++;
                        log.error("重连次数: 【" + serversLoadTimes + "】 ");
                        client.newCall(call.request()).enqueue(this);
                    } else {
                        e.printStackTrace();
                    }
                }

                @Override
                public void onResponse(Call call, Response response) throws IOException {
                    /**
                     * 返回值处理
                     **/
                    if (StrUtil.isNotEmpty(requestAfterGroovy)) {
                        Object[] executeAfterGroovy = new Object[]{upResponseBody, response};
                        executeGroovy(executeAfterGroovy, requestAfterGroovy);
                    }

                    if (response.isSuccessful()) {
                        if (null != response.body()) {
                            log.info("请求后返回数据： " + response.body().string());
                        }
                    } else {
                        log.error("token请求接口地址失败：" + url);
                    }
                    log.info("请求后返回数据： " + response.body().string());
                    // 不论成功与否，记得在处理完响应后关闭响应体以释放资源
                    response.body().close();
                }

            });
        } else {
            Response response = client.newCall(request).execute();//同步
            /**
             * 返回值处理
             **/
            if (StrUtil.isNotEmpty(requestAfterGroovy)) {
                Object[] executeAfterGroovy = new Object[]{upResponseBody, response};
                executeGroovy(executeAfterGroovy, requestAfterGroovy);
            }

            if (response.isSuccessful()) {
                if (null != response.body()) {
                    log.info("请求后返回数据： " + response.body().string());
                }
            } else {
                log.error("请求接口地址失败：" + url);
            }
            response.body().close();
            return response.isSuccessful();
        }
        return isSuccess;
    }

    /**
     * 执行Groovy脚本
     *
     * @param objects Groovy接收的参数
     * @param groovy  要执行的脚本
     * @return
     */
    private static Object executeGroovy(Object[] objects, String groovy) {
        if (!groovy.isEmpty()) {
            GroovyShell groovyShell = new GroovyShell();
            Script executeScript = groovyShell.parse(groovy);
            log.info("执行脚本" + groovy);
            Object executeResult = executeScript.invokeMethod("execute", objects);
            log.info("执行后返回的值：" + executeResult);
            //清除掉GroovyShell、Script等实例，
            groovyShell.getClassLoader().clearCache();
            return executeResult;
        }
        return null;
    }
}